package com.mhh.selary.backend.api.impl.config;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.mhh.sdk.beans.exception.BusinessException;
import com.mhh.sdk.utils.security.AssertTools;
import com.mhh.selary.backend.api.config.ISelaryConfigService;
import com.mhh.selary.backend.config.EnvPropConfig;
import com.mhh.selary.backend.dao.api.config.ISelaryConfigEnvDao;
import com.mhh.selary.backend.dao.api.config.ISelaryConfigPropDao;
import com.mhh.selary.backend.dao.entity.config.SelaryConfigEnvEntity;
import com.mhh.selary.backend.dao.entity.config.SelaryConfigPropEntity;
import com.mhh.selary.backend.dto.IdDTO;
import com.mhh.selary.backend.dto.config.EnvDTO;
import com.mhh.selary.backend.dto.config.EnvSaveDTO;
import com.mhh.selary.backend.dto.config.PropConfigByGroupDTO;
import com.mhh.selary.backend.dto.config.PropConfigDTO;
import com.mhh.selary.backend.dto.config.PropSaveDTO;
import com.mhh.selary.backend.enums.BizErrorEnum;
import com.mhh.selary.backend.enums.PropGroupEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * SelaryConfigEnvServiceImpl
 *
 * @author 人工智能
 * @date 2020-09-02 09:01:15
 */
@Slf4j
@Service
public class SelaryConfigServiceImpl implements ISelaryConfigService {

    @Autowired
    private ISelaryConfigEnvDao selaryConfigEnvDao;

    @Autowired
    private ISelaryConfigPropDao selaryConfigPropDao;

    @Override
    public List<EnvDTO> findAll() {
        return selaryConfigEnvDao.list().stream().map(entity -> EnvDTO.builder().envId(entity.getId()).envName(entity.getEnvName()).build()).collect(Collectors.toList());
    }

    @Override
    public Long saveEnv(EnvSaveDTO dto) {
        if (dto.getId() == null) {
            return this.addEnv(dto);
        }
        return this.editEnv(dto);
    }

    private Long addEnv(EnvSaveDTO dto) {
        LambdaQueryWrapper<SelaryConfigEnvEntity> queryWrapper = new LambdaQueryWrapper<SelaryConfigEnvEntity>().eq(SelaryConfigEnvEntity::getEnvName, dto.getEnvName());
        SelaryConfigEnvEntity envEntity = selaryConfigEnvDao.getOne(queryWrapper);
        //判断环境名称是否存在
        AssertTools.isNull(envEntity, BizErrorEnum.ENV_NAME_EXIST);

        envEntity = new SelaryConfigEnvEntity();
        envEntity.setEnvName(dto.getEnvName());
        selaryConfigEnvDao.save(envEntity);
        return envEntity.getId();
    }

    private Long editEnv(EnvSaveDTO dto) {
        SelaryConfigEnvEntity envEntity = selaryConfigEnvDao.getById(dto.getId());
        //判断环境配置是否存在
        AssertTools.notNull(envEntity, BizErrorEnum.ENV_NAME_NOT_EXIST);

        LambdaQueryWrapper<SelaryConfigEnvEntity> queryWrapper = new LambdaQueryWrapper<SelaryConfigEnvEntity>().eq(SelaryConfigEnvEntity::getEnvName, dto.getEnvName());
        SelaryConfigEnvEntity sameNameEntity = selaryConfigEnvDao.getOne(queryWrapper);
        //判断环境名称是否存在
        if (sameNameEntity != null && !sameNameEntity.getId().equals(dto.getId())) {
            throw new BusinessException(BizErrorEnum.ENV_NAME_EXIST);
        }

        envEntity = new SelaryConfigEnvEntity();
        envEntity.setId(dto.getId());
        envEntity.setEnvName(dto.getEnvName());
        selaryConfigEnvDao.updateById(envEntity);
        return envEntity.getId();
    }

    @Override
    public void delEnv(IdDTO dto) {
        selaryConfigEnvDao.removeById(dto.getId());
    }

    @Override
    public PropConfigByGroupDTO findPropConfigByGroup(Long envId) {
        LambdaQueryWrapper<SelaryConfigPropEntity> queryWrapper = new LambdaQueryWrapper<SelaryConfigPropEntity>().eq(SelaryConfigPropEntity::getEnvId, envId);
        List<SelaryConfigPropEntity> propEntityList = selaryConfigPropDao.list(queryWrapper);
        Map<String, List<SelaryConfigPropEntity>> propGroupMap = propEntityList.stream().collect(Collectors.groupingBy(SelaryConfigPropEntity::getPropGroup));

        Map<String, List<PropConfigDTO>> propConfigMap = Maps.newHashMapWithExpectedSize(4);
        propConfigMap.put(PropGroupEnum.JDBC.getName(), this.collect(PropGroupEnum.JDBC, propGroupMap.get(PropGroupEnum.JDBC.getName())));
        propConfigMap.put(PropGroupEnum.REDIS.getName(), this.collect(PropGroupEnum.REDIS, propGroupMap.get(PropGroupEnum.REDIS.getName())));
        propConfigMap.put(PropGroupEnum.MONGODB.getName(), this.collect(PropGroupEnum.MONGODB, propGroupMap.get(PropGroupEnum.MONGODB.getName())));
        propConfigMap.put(PropGroupEnum.USER_DEFINED.getName(), this.collectUserDefined(PropGroupEnum.USER_DEFINED, propGroupMap.get(PropGroupEnum.USER_DEFINED.getName())));
        return PropConfigByGroupDTO.builder().propConfigMap(propConfigMap).build();
    }

    private List<PropConfigDTO> collect(PropGroupEnum propGroupEnum, List<SelaryConfigPropEntity> propList) {
        final Map<String, SelaryConfigPropEntity> propMap;
        if (CollectionUtils.isNotEmpty(propList)) {
            propMap = propList.stream().collect(Collectors.toMap(SelaryConfigPropEntity::getPropKey, Function.identity()));
        } else {
            propMap = Maps.newHashMapWithExpectedSize(0);
        }
        return propGroupEnum.getBelong().stream().map(propKeyEnum -> {
            String propKey = propKeyEnum.getName();
            SelaryConfigPropEntity propEntity = propMap.get(propKey);
            PropConfigDTO propConfigDTO = new PropConfigDTO();
            if (propEntity != null) {
                propConfigDTO.setPropId(propEntity.getId());
                propConfigDTO.setPropValue(propEntity.getPropValue());
            }
            propConfigDTO.setPropGroup(propGroupEnum.getName());
            propConfigDTO.setPropKey(propKey);
            propConfigDTO.setPropName(propKeyEnum.getDesc());
            return propConfigDTO;
        }).collect(Collectors.toList());
    }

    private List<PropConfigDTO> collectUserDefined(PropGroupEnum propGroupEnum, List<SelaryConfigPropEntity> propList) {
        if (CollectionUtils.isEmpty(propList)) {
            return Lists.newArrayListWithCapacity(0);
        }
        return propList.stream().map(propEntity -> {
            PropConfigDTO propConfigDTO = new PropConfigDTO();
            propConfigDTO.setPropId(propEntity.getId());
            propConfigDTO.setPropValue(propEntity.getPropValue());
            propConfigDTO.setPropGroup(propGroupEnum.getName());
            propConfigDTO.setPropKey(propEntity.getPropKey());
            propConfigDTO.setPropName(propEntity.getPropName());
            return propConfigDTO;
        }).collect(Collectors.toList());
    }

    @Override
    public Long saveProp(PropSaveDTO dto) {
        Long id = null;
        if (dto.getId() == null) {
            if (StringUtils.isBlank(dto.getPropName()) && StringUtils.isBlank(dto.getPropKey()) && StringUtils.isBlank(dto.getPropValue())) {
                return id;
            }
            id = this.insertProp(dto);
        } else {
            id = this.updateProp(dto);
        }
        EnvPropConfig.put(dto.getEnvId(), dto.getPropKey(), dto.getPropValue());
        return id;
    }

    @Override
    public void delProp(IdDTO dto) {
        SelaryConfigPropEntity propEntity = selaryConfigPropDao.getById(dto.getId());
        selaryConfigPropDao.removeById(dto.getId());
        EnvPropConfig.del(propEntity.getEnvId(), propEntity.getPropKey());
    }

    private Long insertProp(PropSaveDTO dto) {
        SelaryConfigPropEntity propEntity = new SelaryConfigPropEntity();
        propEntity.setEnvId(dto.getEnvId());
        propEntity.setPropGroup(dto.getPropGroup());
        propEntity.setPropName(dto.getPropName());
        propEntity.setPropKey(dto.getPropKey());
        propEntity.setPropValue(dto.getPropValue());
        selaryConfigPropDao.save(propEntity);
        return propEntity.getId();
    }

    private Long updateProp(PropSaveDTO dto) {
        UpdateWrapper<SelaryConfigPropEntity> updateWrapper = new UpdateWrapper<>();
        updateWrapper.eq("id", dto.getId());
        updateWrapper.set("prop_name", dto.getPropName());
        updateWrapper.set("prop_key", dto.getPropKey());
        updateWrapper.set("prop_value", dto.getPropValue());
        selaryConfigPropDao.update(new SelaryConfigPropEntity(), updateWrapper);
        return dto.getId();
    }

}
